package com.binarycat.dynamiclimit.initialization;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.method.HandlerMethod;

import com.binarycat.dynamiclimit.annotation.DynamicLimit;
import com.binarycat.dynamiclimit.bean.DynamicLimitData;
import com.binarycat.dynamiclimit.bean.MethodMappingLimitData;
import com.binarycat.dynamiclimit.util.CommonLimitUtils;
import com.google.common.util.concurrent.RateLimiter;

/**
 * 限流器工厂
 * 
 * @author jinbaoji
 * @date 2018/08/15
 */
public class RateLimiterFactory extends AbstractInitializationFactory {

    private static String loclkey = CommonLimitUtils.getLock(RateLimiterFactory.class);;

    /**
     * 注册限流器
     * 
     * @param methodRateLimiterMap
     * @param handlerMethod
     */
    public static void registryRateLimiter(DynamicLimitData dynamicLimitData, HandlerMethod handlerMethod) {
        Map<String, MethodMappingLimitData> methodMappingLimitDataMap = dynamicLimitData.getMethodMappingLimitDataMap();
        
        DynamicLimit dynamicLimit = CommonLimitUtils.getDynamicLimitAnnotation(handlerMethod);
        
        String methodName = handlerMethod.toString();

        // 方法对应的限流器
        MethodMappingLimitData methodMappingLimitData = new MethodMappingLimitData(dynamicLimitData.getMetricRegistry());
        RateLimiter rateLimiter = RateLimiter.create(500, 1, TimeUnit.SECONDS);
        methodMappingLimitData.setRateLimiter(rateLimiter);
        methodMappingLimitData.setDynamicLimitAnnotation(dynamicLimit);
        methodMappingLimitData.setMethodName(methodName);
        methodMappingLimitData.setMetricKey(methodName);

        methodMappingLimitDataMap.put(handlerMethod.toString(), methodMappingLimitData);
    }

    public static MethodMappingLimitData getMethodMappingLimitData(DynamicLimitData limitData, HandlerMethod method,
        HttpServletRequest request) {
        Map<String, MethodMappingLimitData> methodRateLimiterMap = limitData.getMethodMappingLimitDataMap();
        MethodMappingLimitData methodMappingLimitData;
        String ipkey = CommonLimitUtils.getKeyByIpQuarantine(method, request);

        methodMappingLimitData = methodRateLimiterMap.get(ipkey.toString());
        if (methodMappingLimitData == null) {
            synchronized (loclkey) {
                methodMappingLimitData = methodRateLimiterMap.get(ipkey.toString());
                if (methodMappingLimitData == null) {

                    MethodMappingLimitData methodMappingLimitDataNew = new MethodMappingLimitData(limitData.getMetricRegistry());
                    methodMappingLimitDataNew.setRateLimiter(RateLimiter.create(500, 1, TimeUnit.SECONDS));
                    methodMappingLimitDataNew.setDynamicLimitAnnotation(CommonLimitUtils.getDynamicLimitAnnotation(method));
                    methodMappingLimitDataNew.setMethodName(method.toString());
                    methodMappingLimitDataNew.setMetricKey(ipkey);
                    
                    methodRateLimiterMap.put(ipkey.toString(), methodMappingLimitDataNew);
                    methodMappingLimitData = methodMappingLimitDataNew;
                }
            }
        }
        return methodMappingLimitData;
    }
}